package com.lk.common.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 反射工具类
 * @author lijun
 * @since 2018-05-06 
 */
public class ReflectUtil {
	
	private static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
	
	// --------------------------------------------------------------------------------------------------------- method
	/**
	 * 查找指定对象中的所有方法（包括非public方法），也包括父对象和Object类的方法
	 * 
	 * @param obj 被查找的对象，如果为{@code null}返回{@code null}
	 * @param methodName 方法名，如果为空字符串返回{@code null}
	 * @param args 参数
	 * @return 方法
	 * @throws SecurityException 无访问权限抛出异常
	 */
	public static Method getMethodOfObj(Object obj, String methodName, Object... args) throws SecurityException {
		if (null == obj || methodName == null || methodName.equals("")) {
			return null;
		}
		return getMethod(obj.getClass(), methodName, ClassUtil.getClasses(args));
	}

	/**
	 * 忽略大小写查找指定方法，如果找不到对应的方法则返回<code>null</code>
	 * 
	 * @param clazz 类，如果为{@code null}返回{@code null}
	 * @param methodName 方法名，如果为空字符串返回{@code null}
	 * @param paramTypes 参数类型，指定参数类型如果是方法的子类也算
	 * @return 方法
	 * @throws SecurityException 无权访问抛出异常
	 * @since 3.2.0
	 */
	public static Method getMethodIgnoreCase(Class<?> clazz, String methodName, Class<?>... paramTypes) throws SecurityException {
		return getMethod(clazz, true, methodName, paramTypes);
	}

	/**
	 * 查找指定方法 如果找不到对应的方法则返回<code>null</code>
	 * 
	 * @param clazz 类，如果为{@code null}返回{@code null}
	 * @param methodName 方法名，如果为空字符串返回{@code null}
	 * @param paramTypes 参数类型，指定参数类型如果是方法的子类也算
	 * @return 方法
	 * @throws SecurityException 无权访问抛出异常
	 */
	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) throws SecurityException {
		return getMethod(clazz, false, methodName, paramTypes);
	}

	/**
	 * 查找指定方法 如果找不到对应的方法则返回<code>null</code>
	 * 
	 * @param clazz 类，如果为{@code null}返回{@code null}
	 * @param ignoreCase 是否忽略大小写
	 * @param methodName 方法名，如果为空字符串返回{@code null}
	 * @param paramTypes 参数类型，指定参数类型如果是方法的子类也算
	 * @return 方法
	 * @throws SecurityException 无权访问抛出异常
	 * @since 3.2.0
	 */
	public static Method getMethod(Class<?> clazz, boolean ignoreCase, String methodName, Class<?>... paramTypes) throws SecurityException {
		if (null == clazz || StrUtil.isBlank(methodName)) {
			return null;
		}

		final Method[] methods = getMethods(clazz,true);
		if (ArrayUtil.isNotEmpty(methods)) {
			for (Method method : methods) {
				boolean eqs = false;
				if(ignoreCase) {
					eqs = methodName.equalsIgnoreCase(method.getName());
				}else {
					eqs = methodName.equals(method.getName());
				}
				//如果存在方法，判断方法参数是否一致
				if (eqs) {
					if (paramTypes == null || paramTypes.length == 0 || isParameterEquals(method.getParameterTypes(), paramTypes)) {
						return method;
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * 判断两组参数是否一致
	 */
	public static boolean isParameterEquals(Class<?>[] types1, Class<?>[] types2) {
		boolean result = true;
		//长度不一致
		if(types1.length != types2.length) {
			result = false;
		}
		//类型和修饰符必须一致
		for(int i = 0;i<types1.length;i++) {
			types1[i].isAssignableFrom(types2[i]);
		}
		return true;
	}

	/**
	 * 获得指定类中的Public方法名<br>
	 * 去重重载的方法
	 * 
	 * @param clazz 类
	 * @return 方法名Set
	 * @throws SecurityException 安全异常
	 */
	public static Set<String> getMethodNames(Class<?> clazz) throws SecurityException {
		final HashSet<String> methodSet = new HashSet<String>();
		final Method[] methods = getMethods(clazz,true);
		for (Method method : methods) {
			methodSet.add(method.getName());
		}
		return methodSet;
	}

	/**
	 * 获得指定类过滤后的Public方法列表
	 * 
	 * @param clazz 查找方法的类
	 * @param filterNames 过滤掉的方法名称
	 * @return 过滤后的方法列表
	 * @throws SecurityException 安全异常
	 */
	public static Method[] getMethods(Class<?> clazz, Set<String> filterNames) throws SecurityException {
		if (null == clazz) {
			return null;
		}

		final Method[] methods = getMethods(clazz,true);
		if (null == filterNames || filterNames.size() == 0) {
			return methods;
		}

		final List<Method> methodList = new ArrayList<>();
		for (Method method : methods) {
			if(!filterNames.contains(method.getName())) {
				methodList.add(method);
			}
		}
		return methodList.toArray(new Method[methodList.size()]);
	}


	/**
	 * 获得一个类中所有方法列表，直接反射获取，无缓存
	 * 
	 * @param beanClass 类
	 * @param withSuperClassMethods 是否包括父类的方法列表
	 * @return 方法列表
	 * @throws SecurityException 安全检查异常
	 */
	public static Method[] getMethods(Class<?> beanClass, boolean withSuperClassMethods) throws SecurityException {
		Method[] allMethods = null;
		Class<?> searchType = beanClass;
		Method[] declaredMethods;
		while (searchType != null) {
			declaredMethods = searchType.getDeclaredMethods();
			if (null == allMethods) {
				allMethods = declaredMethods;
			} else {
				allMethods = ArrayUtil.append(allMethods, declaredMethods);
			}
			searchType = withSuperClassMethods ? searchType.getSuperclass() : null;
		}
		return allMethods;
	}

	/**
	 * 是否为equals方法
	 * 
	 * @param method 方法
	 * @return 是否为equals方法
	 */
	public static boolean isEqualsMethod(Method method) {
		if (method == null || false == method.getName().equals("equals")) {
			return false;
		}
		final Class<?>[] paramTypes = method.getParameterTypes();
		return (1 == paramTypes.length && paramTypes[0] == Object.class);
	}

	/**
	 * 是否为hashCode方法
	 * 
	 * @param method 方法
	 * @return 是否为hashCode方法
	 */
	public static boolean isHashCodeMethod(Method method) {
		return (method != null && method.getName().equals("hashCode") && method.getParameterTypes().length == 0);
	}

	/**
	 * 是否为toString方法
	 * 
	 * @param method 方法
	 * @return 是否为toString方法
	 */
	public static boolean isToStringMethod(Method method) {
		return (method != null && method.getName().equals("toString") && method.getParameterTypes().length == 0);
	}
	
	
	// --------------------------------------------------------------------------------------------------------- annotation
	/** 
     * 得到某个类中拥有某个注解的所有fileds 
     * @param clazz 你要查找类的class对象
     * @param fields 注意递归
     * @Param excelType 注解类的class对象
     */  
    @SuppressWarnings({ "rawtypes", "unused" })
    private List<Field> getMappedFiled(Class clazz, List<Field> fields,Class annotationType) {  
        if (fields == null) {  
            fields = new ArrayList<Field>();  
        }  
        Field[] allFields = clazz.getDeclaredFields();// 得到所有定义字段  
        // 得到所有field并存放到一个list中.  
        for (Field field : allFields) {  
            if (field.isAnnotationPresent(annotationType)) {
                fields.add(field);
            }  
        }  
        if (clazz.getSuperclass() != null  
                && !clazz.getSuperclass().equals(Object.class)) {  
            getMappedFiled(clazz.getSuperclass(), fields,annotationType);  
        }  
        return fields;  
    }
}
